/***************************************************************************
 *   Copyright (C) 2008 by JBox systems
 *   gabi@shaargiora.co.il
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 ***************************************************************************/

#include <QMessageBox>
#include <QFileDialog>
#include <QFile>
#include <QIcon>
#include <QXmlInputSource>
#include <QDomElement>
#include <QSortFilterProxyModel>

#ifdef Q_OS_WIN32
#include <windows.h>
#endif

#include <boost/filesystem.hpp>
#include <string>
#include <cstdlib>
#include <iostream>

#include "gui.h"
#include "appsmodel.h"
#include "utils.h"
#include "log.h"
#include "importhandler.h"
#include "logwatcher.h"
#include "const.h"
#include "config.h"
#include "helpui.h"

extern std::string path_to_database;

AutoMateUi::AutoMateUi(QWidget* parent) :
	QMainWindow(parent),
	last_database_location(CONFIG.database()) {
	setupUi(this);

	//models
	amodel = new AppsModel(loadApplicationDatabase((boost::filesystem::path( CONFIG.database() ) / "applications").string().c_str()));
	QSortFilterProxyModel *appsProxyModel = new QSortFilterProxyModel(this);
	appsProxyModel->setFilterKeyColumn(-1);
	appsProxyModel->setSourceModel(amodel);

	// Setup table view
	apps_view->setModel(appsProxyModel);
	apps_view->sortByColumn(0, Qt::AscendingOrder);
	apps_view->setColumnHidden(4, true); // This must be set after the model was setup.

	// Watch log file
	logWatcher = new TailWatch(Logger::path(), log_browser);
	config = new ConfigurationUi(this);

	connectUi();
	actionsConnect();
}

void AutoMateUi::setupUi(QMainWindow *widget) {
	Ui::AutoMate::setupUi(widget);

	// Setup donwload option
	download_checkBox = new QCheckBox(widget);
	download_checkBox->setText("Download only");
	action_toolBar->addWidget(download_checkBox);

	// setup quick help
	if(!CONFIG.showHelp()) {
		help_textBrowser->hide();
		hidehelp_checkBox->hide();
	}

	// Setup search bar
	searchEdit = new SearchLineEdit(widget);
	file_toolBar->addSeparator();
	file_toolBar->addWidget(searchEdit);

	// Add progress bar to status bar
	progressName = new QLabel();
	progressName->hide();
	downloadProgressBar = new QProgressBar(widget);
	statusBar()->addPermanentWidget(progressName);
	statusBar()->addPermanentWidget(downloadProgressBar);
	downloadProgressBar->hide();

	// Hide tweaks tab untill ver 0.3
	tabs_widget->removeTab(1);


#ifdef Q_OS_WIN32
	// Setup vista UAC state
	actionVista_UAC_prompt->setChecked(windowsUtils::UACState());
	menuSettings->addAction(actionVista_UAC_prompt);
#endif
}

#ifdef Q_OS_WIN32
void AutoMateUi::setUACState(bool state) {
	windowsUtils::setUACState(state);
	requestReboot();
}

void AutoMateUi::requestReboot() {
	QMessageBox::StandardButton result = QMessageBox::question(this, "Reboot required", "Changing this setting requires a reboot. Reboot now ?", QMessageBox::Yes | QMessageBox::No);
	if (result == QMessageBox::Yes)
		ExitWindowsEx(EWX_REBOOT, 0);
}
#endif

void AutoMateUi::connectUi() {
	connect(clear_log_button, SIGNAL(clicked()), log_browser, SLOT(clear()));
	connect(searchEdit, SIGNAL(textEdited(const QString&)), this, SLOT(filterViews()));
	connect(config, SIGNAL(accepted()), this, SLOT(reloadModelsOnDatabaseChange()));
	connect(hidehelp_checkBox, SIGNAL(toggled(bool)), this, SLOT(hideHelp()));
	connect(this, SIGNAL(start()), this, SLOT(apply()));
}

void AutoMateUi::showQuickHelp() {
	CONFIG.setShowHelp(true);
	hidehelp_checkBox->show();
	hidehelp_checkBox->setChecked(false);
	help_textBrowser->show();
}

void AutoMateUi::actionsConnect() {
	connect(actionAbout, SIGNAL(triggered(bool)), this, SLOT(about()));
	connect(actionNew, SIGNAL(triggered(bool)), this, SLOT(clearSelection()));
	connect(actionApply, SIGNAL(triggered(bool)), this, SLOT(apply()));
	connect(actionLoad, SIGNAL(triggered(bool)), this, SLOT(load()));
	connect(actionSave, SIGNAL(triggered(bool)), this, SLOT(save()));
	connect(actionReload_database, SIGNAL(triggered(bool)), this, SLOT(reloadModels()));
	connect(actionConfiguration, SIGNAL(triggered(bool)), config, SLOT(exec()));
	connect(actionShow_quick_help, SIGNAL(triggered(bool)), this, SLOT(showQuickHelp()));
#ifdef Q_OS_WIN32
	connect(actionVista_UAC_prompt, SIGNAL(triggered(bool)), this, SLOT(setUACState(bool)));
#endif
	connect(actionContents, SIGNAL(triggered(bool)), this, SLOT(showContents()));
}

void AutoMateUi::hideHelp() {
	CONFIG.setShowHelp(false);
}

void AutoMateUi::applySelected() {
	emit start();
}

void AutoMateUi::quit() {
}

void AutoMateUi::about() {
	QMessageBox::about(this, QString("Auto-Mate %1").arg(VERSION.c_str()), QString("<center><b>Auto-Mate %1</b></center>\nAutomates installing applications and performing other actions. A GUI front-end for an application and settings database.").arg(VERSION.c_str()));
}

void AutoMateUi::save() {
	std::vector<Version> vers = amodel->getSelected();
	QDomDocument doc("export");
	QDomElement root = doc.createElement("template");
	doc.appendChild(root);
	for(std::vector<Version>::iterator itr = vers.begin(); itr != vers.end(); ++itr)
	{
		QDomElement inst = doc.createElement("install");
		if (! itr->is_last)
			inst.setAttribute("version",itr->version().c_str());
		QDomText name = doc.createTextNode(itr->name().c_str());
		inst.appendChild(name);
		root.appendChild(inst);
	}
	QString flname = QFileDialog::getSaveFileName(this,"Select file to save", "", "XML files(*.xml)");
	QFile *fl = new QFile(flname);
	if (!fl->open(QIODevice::WriteOnly | QIODevice::Text)) {
		statusBar()->showMessage("Writing to file failed", status_message_delay);
		return;
	}
	fl->write(doc.toString().toLocal8Bit());
	fl->close();
	statusBar()->showMessage("Selection saved", status_message_delay);
}

void AutoMateUi::loadTemplate(std::string filename) {
	ImportHandler *handler = new ImportHandler;

	QFile *fh = new QFile(QString(filename.c_str()));
	if (!(fh->exists()))
		return;

	QXmlInputSource *src = new QXmlInputSource(fh);
	QXmlSimpleReader *reader = new QXmlSimpleReader;
	reader->setContentHandler(handler);
	reader->parse(src);
	std::vector<QModelIndex> toExpand = amodel->setSelected(handler->installList());
	for (unsigned int i = 0; i < toExpand.size(); ++i) {
		apps_view->expand(toExpand[i]);
	}
	apps_view->repaint();
	LOG("Loaded template " << filename)
}

void AutoMateUi::load() {
	QString filename = QFileDialog::getOpenFileName(this,"Select a file to load", "", "XML files(*.xml)");
	if (filename.size() == 0) {
		statusBar()->showMessage("No template selected", status_message_delay);
		return;
	}
	loadTemplate(filename.toStdString());
}

void AutoMateUi::clearSelection() {
	amodel->clearSelection();
	apps_view->repaint();
	statusBar()->showMessage("Selection cleared", status_message_delay);
}

void AutoMateUi::tweak() {

}

void AutoMateUi::apply() {
	std::vector<Version> versions = amodel->getSelected();

	// Nothing selected. do nothing.
	if (versions.size() == 0)
			return;

	// Change the apply action to "stop"
	actionApply->setText("Stop");
	actionApply->setIcon(QIcon(QPixmap(":/icons/abort.png")));
	disconnect(actionApply, 0, this, 0);
	connect(actionApply, SIGNAL(triggered(bool)), this, SIGNAL(abort()));
	downloadProgressBar->show();
	progressName->show();
	emit apply(versions, download_checkBox->isChecked());
}

void AutoMateUi::updateDataReadProgress(int bytesRead, int totalBytes){
	downloadProgressBar->setMaximum(totalBytes);
	downloadProgressBar->setValue(bytesRead);
}

void AutoMateUi::resetApplyAction() {
	actionApply->setText("Apply");
	actionApply->setIcon(QIcon(QPixmap(":/icons/install.png")));
	downloadProgressBar->reset();
	downloadProgressBar->hide();
	progressName->setText("");
	progressName->hide();
	disconnect(actionApply, 0, this, 0);
	connect(actionApply, SIGNAL(triggered(bool)), this, SLOT(apply()));
	// FIXME this is ugly. should only refresh changes on downloaded/applied applications
	reloadModels();
}

void AutoMateUi::updateStatusMessage(QString message) {
	statusBar()->showMessage(message, status_message_delay);
}

void AutoMateUi::filterViews() {
	static_cast<QSortFilterProxyModel*>(apps_view->model())->setFilterRegExp(QRegExp(searchEdit->text(), Qt::CaseInsensitive, QRegExp::FixedString));
}

void AutoMateUi::reloadModels() {
	// Save selection
	std::vector<Version> selection = amodel->getSelected();
	static_cast<QSortFilterProxyModel*>(apps_view->model())->setSourceModel(new AppsModel(std::vector<Application>()));
	amodel = new AppsModel(loadApplicationDatabase((boost::filesystem::path( CONFIG.database() ) / "applications").string().c_str()));
	// Reload selection
	amodel->setSelected(selection);
	static_cast<QSortFilterProxyModel*>(apps_view->model())->setSourceModel(amodel);
// 	qDebug() << "Database reloaded..";
}

// FIXME ugly hack. this should be elsewhere
void AutoMateUi::reloadModelsOnDatabaseChange() {
	if (QString::compare(QString(last_database_location.c_str()), QString(CONFIG.database().c_str()), Qt::CaseInsensitive) != 0) {
		last_database_location = CONFIG.database();
		reloadModels();
	}
}

void AutoMateUi::updateProgressName(QString name) {
	progressName->setText(name);
}

void AutoMateUi::showContents() {
	HelpUi help(this);
	help.exec();
}
